#include <D3D11.h>
#include <D3DX11.h>
#include "DXUT.h"
#include "PrecompiledHeader.h"
#include "Shader.h"
#include "VertexDecl.h"
#include "Buffer.h"
#include "VertexStructs.h"
#include "CBufferStructs.h"
#include "Pipeline.h"
using namespace d3d11;
#include "ModelObject.h"

using namespace modelobject;

ModelObject::ModelObject(IDirect3DDevice9* pd3dDevice)
{
	m_pD3DDevice9 = pd3dDevice;
	m_pD3DDevice11 = DXUTGetD3D11Device();
	
	// position vertices.
	VPosColour triangleVertices[] =
	{
        D3DXVECTOR3( 0.0f, 0.5f, 0.5f ), D3DXVECTOR4( 1.0f, 0.0f, 0.0f, 1.0f),		// mid top   - RED
        D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),D3DXVECTOR4( 0.0f, 1.0f, 0.0f, 1.0f),		// low right - GREEN
        D3DXVECTOR3( -0.5f, -0.5f, 0.5f ), D3DXVECTOR4( 0.0f, 0.0f, 1.0f, 1.0f),	// low left  - BLUE
		D3DXVECTOR3( 1.0f, 0.5f, 0.5f ),D3DXVECTOR4( 0.8f, 1.0f, 0.8f, 1.0f)		// top right - GREEN / GREY
	};

	// indices. - make sure winding is clockwise to create front facing polygons
	unsigned int indices[] = { 0, 1, 2, 0, 3, 1}; // 0,2,1,0,1,3 - will NOT display anything in FILL_MODE_SOLID
	//unsigned int indices[] = { 1,3,0,2,1,0 }; // in reverse - should NOT show...!
	
	if (m_pD3DDevice9)
	{
		// for directx9
		// create a test triangle and fill it with data
		m_pTestTriangle = new Buffer(D3DFVF_XYZ | D3DFVF_DIFFUSE, 
									D3DUSAGE_WRITEONLY, 
									D3DPOOL_DEFAULT, 
									sizeof(VPosColour), 
									sizeof(VPosColour) * 4, 
									triangleVertices);

		m_pIndexBuffer = new Buffer(D3DFMT_INDEX32,
									D3DUSAGE_WRITEONLY,
									D3DPOOL_DEFAULT,
									6,
									sizeof(unsigned int) * 6,
									indices);

	}
	else
	{
		// for directx11
		// create a test triangle and fill it with data

		// create vertex buffer
		m_pTestTriangle = new Buffer(D3D11_USAGE_DEFAULT, 
										sizeof( VPosColour ), 
										sizeof( VPosColour ) * 4,
										triangleVertices);	

		// create index buffer
		m_pIndexBuffer = new Buffer(D3D11_USAGE_DEFAULT,
									sizeof(unsigned int) * 6,
									DXGI_FORMAT_R32_UINT,
									6,
									indices);

		// create constant buffers
		m_pNeverChanges = new Buffer(D3D11_USAGE_DEFAULT,
									sizeof(CBNeverChanges));

		m_pChangesOnResize = new Buffer(D3D11_USAGE_DEFAULT,
									sizeof(CBChangesOnResize));

		m_pChangesEveryFrame = new Buffer(D3D11_USAGE_DEFAULT,
									sizeof(CBChangesEveryFrame));


// TODO - move this stuff into pipeline
		// set up constant buffers
//		m_matWorld = XMMatrixIdentity();
		D3DXMatrixIdentity(&m_matWorld);

		// initialize the view matrix
		D3DXVECTOR3 Eye = D3DXVECTOR3(0.0f, 3.0f, -6.0f );
		D3DXVECTOR3 At = D3DXVECTOR3(0.0f, 1.0f, 0.0f );
		D3DXVECTOR3 Up = D3DXVECTOR3(0.0f, 1.0f, 0.0f );
		D3DXMatrixLookAtLH(&m_matView, &Eye, &At, &Up);

		CBNeverChanges cbNeverChanges;
		D3DXMatrixTranspose(&cbNeverChanges.mView, &m_matView );
		DXUTGetD3D11DeviceContext()->UpdateSubresource( m_pNeverChanges->getBuffer(), 0, NULL, &cbNeverChanges, 0, 0 );

		// intialize the projection matrix
		D3DXMatrixPerspectiveFovLH( &m_matProjection,0.785398163f, DXUTGetWindowWidth() / (FLOAT)DXUTGetWindowHeight(), 0.1f, 100.0f);

		CBChangesOnResize cbChangesOnResize;
		D3DXMatrixTranspose(&cbChangesOnResize.mProjection, &m_matProjection );
		DXUTGetD3D11DeviceContext()->UpdateSubresource( m_pChangesOnResize->getBuffer(), 0, NULL, &cbChangesOnResize, 0, 0 );
	}

	// construct the default pipeline
	m_pPipeline = new Pipeline();

	if (m_pD3DDevice9)
	{
		m_pPipeline->CreatePixelShader(L"basicPS2.hlsl", NULL);
	}
	else
	{
		m_pPipeline->CreateVertexShader(L"basicVS.hlsl", "BasicVS_modelobject");
		m_pPipeline->PushVertexData(VertexData::POSITION, DXGI_FORMAT_R32G32B32_FLOAT, 12);
		m_pPipeline->PushVertexData(VertexData::COLOUR, DXGI_FORMAT_R32G32B32A32_FLOAT, 16);
		m_pPipeline->CreateInputLayout();
		m_pPipeline->CreatePixelShader(L"basicPS.hlsl", "BasicPS_modelobject");	
	}
}

ModelObject::~ModelObject()
{
	SAFE_DELETE(m_pTestTriangle);
	SAFE_DELETE(m_pIndexBuffer);
	SAFE_DELETE(m_pPipeline);
	SAFE_DELETE(m_pNeverChanges);
	SAFE_DELETE(m_pChangesOnResize);
	SAFE_DELETE(m_pChangesEveryFrame);

}

void ModelObject::Render(void)
{
	m_pPipeline->EnablePipeline();
	m_pTestTriangle->SetAsVertexBuffer();
	m_pIndexBuffer->SetAsIndexBuffer();

	// get time
	static float t = 0.0f;
	t += (float) XM_PI * 0.0125f;
	
	D3DXMatrixRotationY( &m_matWorld, t );
	CBChangesEveryFrame cb;
	D3DXMatrixTranspose( &cb.mWorld, &m_matWorld );
	DXUTGetD3D11DeviceContext()->UpdateSubresource( m_pChangesEveryFrame->getBuffer(), 0, NULL, &cb, 0, 0 );

	// update constant buffers.
	DXUTGetD3D11DeviceContext()->VSSetConstantBuffers( 0, 1, m_pNeverChanges->getBufferConst() );
	DXUTGetD3D11DeviceContext()->VSSetConstantBuffers( 1, 1, m_pChangesOnResize->getBufferConst() );
	DXUTGetD3D11DeviceContext()->VSSetConstantBuffers( 2, 1, m_pChangesEveryFrame->getBufferConst() );

//	DXUTGetD3D11DeviceContext()->PSSetConstantBuffers( 0, 1, m_pNeverChanges->getBuffer() );

	if (m_pD3DDevice9 == NULL)
	{
		// for directX 11
		//DXUTGetD3D11DeviceContext()->Draw(3,0);
		DXUTGetD3D11DeviceContext()->DrawIndexed(6,0,0);
	}
	else
	{
		// for directX 9
//		m_pD3DDevice9->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
		m_pD3DDevice9->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,6,0,2);
	}
}